// These are meant to be typed into the REPL. You can also run
// scala -Xnojline < 9_transfer.sc to run them all at once.

// This invariant Pair class doesn't allow conversion of a 
// Pair[Student] to a Pair[Person]

class Person1(name: String) {
  override def toString = getClass.getName + " " + name
}

class Student1(name: String) extends Person1(name)

class Pair1[T](val first: T, val second: T) {
  override def toString = "(" + first + "," + second + ")"
}

def makeFriends1(p: Pair1[Person1]) =
  p.first + " and " + p.second + " are now friends."



val fred1 = new Student1("Fred")
val wilma = new Student1("Wilma")

val studentPair1 = new Pair1(fred1, wilma)//这两个对象为Student1类型，而Pair1接收的是Person1的类型

makeFriends1(studentPair1) // Error

// 需要定义正向形变
// Making Pair covariant allows conversion of a 
// Pair[Student] to a Pair[Person]

class Pair[+T](val first: T, val second: T) {  //可以传入Person1的子类Student1，，即可以在传入T类型的元素处，传入T的子类型
  override def toString = "(" + first + "," + second + ")"
}

def makeFriends(p: Pair[Person1]) =
  p.first + " and " + p.second + " are now friends."

val studentPair = new Pair(fred1, wilma)

makeFriends(studentPair) // OK


/**
  * 负向协变
  * @tparam T
  */
// This contravariant Friend trait allows conversion of a 
// Friend[Person] to a Friend[Student]
trait Friend[-T] { ////可以传入T类型的父类
  def befriend(someone: T)
}

class Person(name: String) extends Friend[Person] {
  override def toString = getClass.getName + " " + name
  def befriend(someone: Person) {
      this + " and " + someone + " are now friends."
  }
}

class Student(name: String) extends Person(name)


def makeFriendWith(s: Student, f: Friend[Student]) { f.befriend(s) }



val susan = new Student("Susan")
val fred = new Person("Fred")

makeFriendWith(susan, fred) // Ok, Fred is a Friend of any person




// A unary function has variance Function1[-A, +R]

def friends(students: Array[Student], find: Function1[Student, Person]) =
  for (s <- students) yield find(s)

def friends1(students: Array[Student], find: Student => Person) =
  for (s <- students) yield find(s)

def findFred(s: Student) = new Person("Fred")

friends(Array(susan, new Student("Barney")), findFred)